"""
Redis工具类 - 与node-server保持一致的接口和缓存机制
"""
import json
import time
from typing import List, Dict, Optional, Any, Set
from datetime import datetime, timedelta
import hashlib

# 自定义JSON编码器，处理datetime对象
class DateTimeEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, datetime):
            return obj.isoformat()
        return super().default(obj)

# 缓存键常量定义 - 严格按照node-server规范
class RedisKeys:
    # 视频相关缓存键
    VIDEO_DETAIL = 'video:detail:{video_id}'
    VIDEO_VIEW_COUNT = 'video:view:{video_id}'
    VIEW_COUNT_KEY = 'video:view:{video_id}'  # 兼容原有的VIEW_COUNT_KEY使用
    
    # 列表缓存键
    VIDEOS_RECOMMENDED_ZSET = 'videos:recommended:zset'  # 修改为与node-server一致
    VIDEOS_HOT_ZSET = 'videos:hot:zset'  # 修改为与node-server一致
    VIDEOS_LATEST_CACHE = 'videos:latest:page:{page}:size:{pageSize}'  # 修改为与node-server一致
    VIDEOS_SLIDE_CACHE = 'videos:slide'  # 修改为与node-server一致
    VIDEOS_CATEGORY_CACHE = 'videos:category:{categoryId}:page:{page}:size:{pageSize}'  # 修改为与node-server一致
    
    # 观看次数更新队列
    VIEW_COUNT_UPDATE_QUEUE = 'video:view:update:queue'
    
    # 布隆过滤器
    BLOOM_FILTER = 'video:bloom:filter'
    
    # 兼容原有键名
    HOT_VIDEO_KEY = 'videos:hot:zset'  # 兼容原方法调用
    RECOMMEND_VIDEO_KEY = 'videos:recommended:zset'  # 兼容原方法调用
    LATEST_VIDEO_KEY = 'videos:latest:page:1:size:10'  # 兼容原方法调用
    SLIDE_VIDEO_KEY = 'videos:slide'  # 兼容原方法调用
    VIDEO_DETAIL_KEY = 'video:detail:{video_id}'  # 兼容原方法调用
    VIDEO_CATEGORY_KEY = 'videos:category:{categoryId}:page:{page}:size:{pageSize}'  # 兼容原方法调用
    VIDEO_VIEW_KEY = 'video:view'  # 兼容原方法调用

# 缓存过期时间定义（秒）
class CacheExpiry:
    # 视频详情缓存
    VIDEO_DETAIL = 1800  # 30分钟
    VIDEO_DETAIL_EXPIRY = 1800  # 兼容原字段名
    
    # 列表缓存
    VIDEOS_RECOMMENDED = 3600  # 1小时
    RECOMMEND_VIDEO_EXPIRE = 3600  # 兼容原字段名
    RECOMMEND_VIDEO_EXPIRY = 3600  # 兼容原字段名
    
    VIDEOS_HOT = 1800  # 30分钟
    HOT_VIDEO_EXPIRE = 1800  # 兼容原字段名
    HOT_VIDEO_EXPIRY = 1800  # 兼容原字段名
    
    VIDEOS_LATEST = 900  # 15分钟
    NEW_VIDEO_EXPIRE = 900  # 兼容原字段名
    LATEST_VIDEO_EXPIRY = 900  # 兼容原字段名
    
    VIDEOS_SLIDE = 3600  # 1小时
    SLIDE_VIDEO_EXPIRE = 3600  # 兼容原字段名
    SLIDE_VIDEO_EXPIRY = 3600  # 兼容原字段名
    
    VIDEOS_CATEGORY = 900  # 15分钟
    VIDEO_CATEGORY_EXPIRY = 900  # 兼容原字段名
    
    # 观看次数计数器
    VIDEO_VIEW_COUNT = 86400  # 24小时
    
    # 布隆过滤器
    BLOOM_FILTER = 604800  # 7天


class RedisUtils:
    """
    Redis工具类 - 与node-server保持一致的接口和缓存机制
    """
    
    def __init__(self):
        # 导入redis_client
        from app.config.redis import redis_client
        self.redis_client = redis_client
        
        # 观看次数更新队列配置
        self.view_update_queue = RedisKeys.VIEW_COUNT_UPDATE_QUEUE
        self.pending_view_updates = set()  # 保持内存队列兼容
        self.view_update_timer = None
        self.view_count_update_config = {
            'interval': 60000,  # 1分钟
            'maxQueueSize': 1000
        }
        # 布隆过滤器配置
        self.bloom_filter_size = 1000000  # 过滤器大小
        self.bloom_filter_hash_count = 7  # 哈希函数数量
        self.bloom_filter_key = RedisKeys.BLOOM_FILTER
        
        # 确保布隆过滤器存在
        if self.redis_client:
            try:
                # 检查布隆过滤器是否已初始化
                if not self.redis_client.exists(self.bloom_filter_key):
                    self.redis_client.set(self.bloom_filter_key, '', ex=CacheExpiry.BLOOM_FILTER)
            except Exception as e:
                print(f"初始化布隆过滤器失败: {str(e)}")
    
    # 缓存基础操作 - 与node-server命名一致
    def setCache(self, key: str, value: Any, expire: int = None) -> bool:
        """
        设置缓存 - 与node-server命名一致
        """
        try:
            if not self.redis_client:
                return False
                
            # 序列化值，使用自定义编码器处理datetime对象
            serialized_value = json.dumps(value, ensure_ascii=False, cls=DateTimeEncoder)
            
            if expire:
                return self.redis_client.setex(key, expire, serialized_value)
            else:
                return self.redis_client.set(key, serialized_value)
        except Exception as e:
            print(f"设置缓存失败: {str(e)}")
            return False
            
    def getCache(self, key: str) -> Optional[Any]:
        """
        获取缓存 - 与node-server命名一致
        """
        try:
            if not self.redis_client:
                return None
                
            value = self.redis_client.get(key)
            if value:
                return json.loads(value)
            return None
        except Exception as e:
            print(f"获取缓存失败: {str(e)}")
            return None
            
    def deleteCache(self, key: str) -> bool:
        """
        删除缓存 - 与node-server命名一致
        """
        try:
            if not self.redis_client:
                return False
                
            return bool(self.redis_client.delete(key))
        except Exception as e:
            print(f"删除缓存失败: {str(e)}")
            return False
            
    def deleteCachePattern(self, pattern: str) -> int:
        """
        删除匹配模式的所有缓存 - 与node-server命名一致
        """
        try:
            if not self.redis_client:
                return 0
                
            keys = self.redis_client.keys(pattern)
            if keys:
                return self.redis_client.delete(*keys)
            return 0
        except Exception as e:
            print(f"批量删除缓存失败: {str(e)}")
            return 0
    
    # ZSET操作方法
    def zadd(self, key: str, mapping: Dict[str, float]) -> int:
        """
        添加元素到有序集合
        """
        try:
            if not self.redis_client:
                return 0
                
            return self.redis_client.zadd(key, mapping)
        except Exception as e:
            print(f"ZADD操作失败: {str(e)}")
            return 0
    
    def zrange(self, key: str, start: int, end: int, withscores: bool = False) -> List[Any]:
        """
        获取有序集合范围内的元素
        """
        try:
            if not self.redis_client:
                return []
                
            results = self.redis_client.zrange(key, start, end, withscores=withscores)
            if withscores:
                return [(json.loads(item), score) for item, score in results]
            return [json.loads(item) for item in results]
        except Exception as e:
            print(f"获取有序集合失败: {str(e)}")
            return []
            
    def zrevrange(self, key: str, start: int, end: int, withscores: bool = False) -> List[Any]:
        """
        按分数从高到低获取有序集合范围内的元素
        """
        try:
            if not self.redis_client:
                return []
                
            results = self.redis_client.zrevrange(key, start, end, withscores=withscores)
            if withscores:
                return [(json.loads(item), score) for item, score in results]
            return [json.loads(item) for item in results]
        except Exception as e:
            print(f"获取有序集合失败: {str(e)}")
            return []
    
    def incr(self, key: str) -> Optional[int]:
        """
        递增计数器 - 与node-server命名一致
        """
        try:
            if not self.redis_client:
                return None
                
            return self.redis_client.incr(key)
        except Exception as e:
            print(f"递增计数器失败: {str(e)}")
            return None
    
    # 兼容原方法
    def set_cache(self, key: str, value: Any, expiry: int) -> bool:
        """兼容原有set_cache方法"""
        return self.setCache(key, value, expiry)
        
    def get_cache(self, key: str) -> Optional[Any]:
        """兼容原有get_cache方法"""
        return self.getCache(key)
        
    def delete_cache(self, key: str) -> int:
        """兼容原有delete_cache方法"""
        result = self.deleteCache(key)
        return 1 if result else 0
        
    def delete_cache_pattern(self, pattern: str) -> int:
        """兼容原有delete_cache_pattern方法"""
        return self.deleteCachePattern(pattern)
        
    def increment_counter(self, key: str, amount: int = 1) -> int:
        """兼容原有increment_counter方法"""
        result = self.incr(key)
        return result if result is not None else 0
        
    # 静态方法 - 获取所有需要同步观看次数的视频ID
    @classmethod
    def get_all_video_ids_for_sync(cls) -> List[int]:
        """
        获取所有需要同步观看次数的视频ID
        
        Returns:
            视频ID列表
        """
        try:
            # 导入redis_client
            from app.config.redis import redis_client
            
            # 使用SCAN命令查找所有观看次数键
            pattern = RedisKeys.VIEW_COUNT_KEY.format('*')
            video_ids = []
            
            # 迭代SCAN结果
            for key in redis_client.scan_iter(match=pattern):
                # 从键名中提取视频ID
                # 键名格式: video:view:{video_id}
                parts = key.decode('utf-8').split(':')
                if len(parts) >= 3:
                    video_ids.append(int(parts[2]))
            
            return video_ids
        except Exception as e:
            print(f"获取需要同步的视频ID失败: {str(e)}")
            return []
    
    # 布隆过滤器操作 - 与node-server命名一致
    def _get_bloom_filter_positions(self, key: str) -> List[int]:
        """
        获取布隆过滤器中键的哈希位置（内部方法）
        """
        positions = []
        for i in range(self.bloom_filter_hash_count):
            # 使用不同的哈希种子
            hash_obj = hashlib.md5(f"{key}:{i}".encode())
            position = int(hash_obj.hexdigest(), 16) % self.bloom_filter_size
            positions.append(position)
        return positions
        
    def add_to_bloom_filter(self, key: str) -> bool:
        """
        添加键到布隆过滤器 - 与node-server命名一致
        """
        try:
            if not self.redis_client:
                return False
                
            positions = self._get_bloom_filter_positions(key)
            pipe = self.redis_client.pipeline()
            
            for position in positions:
                pipe.setbit(self.bloom_filter_key, position, 1)
            
            pipe.execute()
            return True
        except Exception as e:
            print(f"添加到布隆过滤器失败: {str(e)}")
            return False
            
    def check_bloom_filter(self, key: str) -> bool:
        """
        检查键是否在布隆过滤器中 - 与node-server命名一致
        """
        try:
            if not self.redis_client:
                return True  # Redis不可用时，不进行过滤
                
            positions = self._get_bloom_filter_positions(key)
            pipe = self.redis_client.pipeline()
            
            for position in positions:
                pipe.getbit(self.bloom_filter_key, position)
            
            results = pipe.execute()
            
            # 只有当所有位置都为1时，才认为可能存在
            return all(results)
        except Exception as e:
            print(f"布隆过滤器检查失败: {str(e)}")
            return True  # 出错时不进行过滤
    
    # 兼容原有布隆过滤器方法
    def get_bloom_filter_positions(self, bloom_key: str, value: str) -> List[int]:
        """兼容原有get_bloom_filter_positions方法"""
        return self._get_bloom_filter_positions(value)
        
    def bloom_add(self, bloom_key: str, value: str) -> bool:
        """兼容原有bloom_add方法"""
        return self.add_to_bloom_filter(value)
        
    def bloom_check(self, bloom_key: str, value: str) -> bool:
        """兼容原有bloom_check方法"""
        return self.check_bloom_filter(value)

    # 视频观看次数相关 - 与node-server命名一致
    def updateVideoViewCount(self, video_id: int) -> Optional[int]:
        """
        更新视频观看次数 - 与node-server命名一致
        """
        try:
            if not self.redis_client:
                return None
                
            # 递增观看次数
            counter_key = RedisKeys.VIDEO_VIEW_COUNT.format(video_id=video_id)
            new_count = self.incr(counter_key)
            
            # 设置过期时间
            self.redis_client.expire(counter_key, CacheExpiry.VIDEO_VIEW_COUNT)
            
            # 添加到更新队列（使用Set数据结构）
            self.redis_client.sadd(self.view_update_queue, video_id)
            
            # 同时维护内存队列以兼容原有代码
            if len(self.pending_view_updates) < self.view_count_update_config['maxQueueSize']:
                self.pending_view_updates.add(video_id)
            
            return new_count
        except Exception as e:
            print(f"更新视频观看次数失败: {str(e)}")
            return None
            
    def getVideoViewCount(self, video_id: int) -> Optional[int]:
        """
        获取视频观看次数 - 与node-server命名一致
        """
        try:
            if not self.redis_client:
                return None
                
            counter_key = RedisKeys.VIDEO_VIEW_COUNT.format(video_id=video_id)
            count = self.redis_client.get(counter_key)
            return int(count) if count else 0
        except Exception as e:
            print(f"获取视频观看次数失败: {str(e)}")
            return None
            
    def get_cached_category_videos(self, category_id: int, page: int = 1, page_size: int = 10) -> Optional[List[Dict]]:
        """
        获取分类视频缓存
        
        Args:
            category_id: 分类ID
            page: 页码
            page_size: 每页数量
            
        Returns:
            缓存的视频列表，如果没有缓存则返回None
        """
        try:
            # 构建缓存键
            cache_key = RedisKeys.VIDEOS_CATEGORY_CACHE.format(
                categoryId=category_id, 
                page=page, 
                pageSize=page_size
            )
            # 获取缓存数据
            return self.getCache(cache_key)
        except Exception as e:
            print(f"获取分类视频缓存失败: {str(e)}")
            return None
    
    def cache_category_videos(self, category_id: int, videos: List[Dict], page: int = 1, page_size: int = 10, expiry: int = None) -> bool:
        """
        缓存分类视频数据
        
        Args:
            category_id: 分类ID
            videos: 视频列表
            page: 页码
            page_size: 每页数量
            expiry: 过期时间
            
        Returns:
            是否缓存成功
        """
        try:
            # 构建缓存键
            cache_key = RedisKeys.VIDEOS_CATEGORY_CACHE.format(
                categoryId=category_id, 
                page=page, 
                pageSize=page_size
            )
            # 使用默认过期时间
            if expiry is None:
                expiry = CacheExpiry.VIDEOS_CATEGORY
            # 设置缓存
            return self.setCache(cache_key, videos, expiry)
        except Exception as e:
            print(f"缓存分类视频失败: {str(e)}")
            return False
    
    def getPendingViewUpdates(self) -> List[int]:
        """
        获取所有待更新的视频ID - 与node-server命名一致
        """
        try:
            if not self.redis_client:
                return []
                
            # 获取所有待更新的视频ID（Set数据结构）
            video_ids = self.redis_client.smembers(self.view_update_queue)
            if video_ids:
                # 转换为整数列表
                return [int(video_id) for video_id in video_ids]
            return []
        except Exception as e:
            print(f"获取待更新视频ID失败: {str(e)}")
            return []
    
    def clearPendingViewUpdates(self, video_ids: List[int]) -> bool:
        """
        清除待更新的视频ID
        """
        try:
            if not self.redis_client or not video_ids:
                return False
                
            # 从Set中删除已处理的视频ID
            self.redis_client.srem(self.view_update_queue, *video_ids)
            return True
        except Exception as e:
            print(f"清除待更新视频ID失败: {str(e)}")
            return False
    
    # 兼容原有方法
    def update_video_view_count(self, video_id: int) -> bool:
        """兼容原有update_video_view_count方法"""
        result = self.updateVideoViewCount(video_id)
        return result is not None
        
    def get_video_view_count(self, video_id: int) -> int:
        """兼容原有get_video_view_count方法"""
        result = self.getVideoViewCount(video_id)
        return result if result is not None else 0
        
    def flush_pending_view_updates(self) -> Set[int]:
        """兼容原有flush_pending_view_updates方法"""
        updates = self.pending_view_updates.copy()
        self.pending_view_updates.clear()
        return updates
    
    # 视频缓存操作 - 与node-server命名一致
    def cacheRecommendedVideos(self, videos: List[Dict[str, Any]]) -> bool:
        """
        缓存推荐视频到ZSET - 与node-server命名一致
        """
        try:
            if not self.redis_client or not videos:
                return False
                
            # 构建ZSET映射
            mapping = {}
            for video in videos:
                # 使用收藏数作为score
                score = video.get('collect_count', 0)
                mapping[json.dumps(video, ensure_ascii=False)] = score
            
            # 添加到ZSET
            self.zadd(RedisKeys.VIDEOS_RECOMMENDED_ZSET, mapping)
            return True
        except Exception as e:
            print(f"缓存推荐视频失败: {str(e)}")
            return False
            
    def getCachedRecommendedVideos(self, start: int, end: int) -> List[Dict[str, Any]]:
        """
        从ZSET获取推荐视频 - 与node-server命名一致
        """
        try:
            if not self.redis_client:
                return []
                
            # 从ZSET获取数据（按score降序）
            videos_data = self.zrevrange(RedisKeys.VIDEOS_RECOMMENDED_ZSET, start, end)
            
            # 反序列化
            videos = []
            for video_json in videos_data:
                videos.append(json.loads(video_json))
            
            return videos
        except Exception as e:
            print(f"获取缓存推荐视频失败: {str(e)}")
            return []
            
    def cacheHotVideos(self, videos: List[Dict[str, Any]]) -> bool:
        """
        缓存热门视频到ZSET - 与node-server命名一致
        """
        try:
            if not self.redis_client or not videos:
                return False
                
            # 构建ZSET映射
            mapping = {}
            for video in videos:
                # 使用观看次数作为score
                score = video.get('view_count', 0)
                mapping[json.dumps(video, ensure_ascii=False)] = score
            
            # 添加到ZSET
            self.zadd(RedisKeys.VIDEOS_HOT_ZSET, mapping)
            return True
        except Exception as e:
            print(f"缓存热门视频失败: {str(e)}")
            return False
            
    def getCachedHotVideos(self, start: int, end: int) -> List[Dict[str, Any]]:
        """
        从ZSET获取热门视频 - 与node-server命名一致
        """
        try:
            if not self.redis_client:
                return []
                
            # 从ZSET获取数据（按score降序）
            videos_data = self.zrevrange(RedisKeys.VIDEOS_HOT_ZSET, start, end)
            
            # 反序列化
            videos = []
            for video_json in videos_data:
                video = json.loads(video_json)
                # 更新观看次数为Redis中的最新值
                video_id = video.get('id')
                if video_id:
                    view_count = self.getVideoViewCount(video_id)
                    if view_count is not None:
                        video['view_count'] = view_count
                videos.append(video)
            
            return videos
        except Exception as e:
            print(f"获取缓存热门视频失败: {str(e)}")
            return []
            
    def cacheLatestVideos(self, videos: List[Dict[str, Any]], page: int = 1, pageSize: int = 10) -> bool:
        """
        缓存最新视频 - 与node-server命名一致
        """
        try:
            if not self.redis_client or not videos:
                return False
                
            cache_key = RedisKeys.VIDEOS_LATEST_CACHE.format(page=page, pageSize=pageSize)
            return self.setCache(cache_key, videos, CacheExpiry.VIDEOS_LATEST)
        except Exception as e:
            print(f"缓存最新视频失败: {str(e)}")
            return False
            
    def getCachedLatestVideos(self, page: int = 1, pageSize: int = 10) -> Optional[List[Dict[str, Any]]]:
        """
        获取缓存的最新视频 - 与node-server命名一致
        """
        try:
            if not self.redis_client:
                return None
                
            cache_key = RedisKeys.VIDEOS_LATEST_CACHE.format(page=page, pageSize=pageSize)
            return self.getCache(cache_key)
        except Exception as e:
            print(f"获取缓存最新视频失败: {str(e)}")
            return None
            
    def cacheSlideVideos(self, videos: List[Dict[str, Any]]) -> bool:
        """
        缓存幻灯片视频 - 与node-server命名一致
        """
        try:
            if not self.redis_client or not videos:
                return False
                
            cache_key = RedisKeys.VIDEOS_SLIDE_CACHE
            return self.setCache(cache_key, videos, CacheExpiry.VIDEOS_SLIDE)
        except Exception as e:
            print(f"缓存幻灯片视频失败: {str(e)}")
            return False
            
    def getCachedSlideVideos(self) -> Optional[List[Dict[str, Any]]]:
        """
        获取缓存的幻灯片视频 - 与node-server命名一致
        """
        try:
            if not self.redis_client:
                return None
                
            cache_key = RedisKeys.VIDEOS_SLIDE_CACHE
            return self.getCache(cache_key)
        except Exception as e:
            print(f"获取缓存幻灯片视频失败: {str(e)}")
            return None
    
    def syncViewCountToDB(self, db, video_ids: List[int]) -> bool:
        """
        同步观看次数到数据库 - 与node-server命名一致
        """
        try:
            if not self.redis_client or not db or not video_ids:
                return False
                
            # 批量获取观看次数
            pipe = self.redis_client.pipeline()
            for video_id in video_ids:
                pipe.get(RedisKeys.VIDEO_VIEW_COUNT.format(video_id=video_id))
            
            view_counts = pipe.execute()
            
            # 批量更新数据库
            for i, video_id in enumerate(video_ids):
                count = view_counts[i]
                if count:
                    # 更新数据库中的观看次数
                    query = "UPDATE videos SET view_count = view_count + :count WHERE id = :video_id"
                    db.session.execute(query, {'count': int(count), 'video_id': video_id})
                    
                    # 重置Redis计数器
                    self.redis_client.delete(RedisKeys.VIDEO_VIEW_COUNT.format(video_id=video_id))
            
            # 提交事务
            db.session.commit()
            
            # 清除相关缓存
            patterns = [
                RedisKeys.VIDEOS_HOT_ZSET,
                RedisKeys.VIDEOS_LATEST_CACHE.format(page='*', pageSize='*'),
                RedisKeys.VIDEO_DETAIL.format(video_id='*')
            ]
            
            for pattern in patterns:
                self.deleteCachePattern(pattern)
            
            # 清除待更新队列
            self.clearPendingViewUpdates(video_ids)
            
            return True
        except Exception as e:
            if 'db' in locals():
                db.session.rollback()
            print(f"同步观看次数到数据库失败: {str(e)}")
            return False
    
    def cacheVideoDetail(self, video_id: int, video_data: Dict[str, Any]) -> bool:
        """
        缓存视频详情 - 与node-server命名一致
        """
        try:
            if not self.redis_client:
                return False
                
            cache_key = RedisKeys.VIDEO_DETAIL.format(video_id=video_id)
            return self.setCache(cache_key, video_data, CacheExpiry.VIDEO_DETAIL)
        except Exception as e:
            print(f"缓存视频详情失败: {str(e)}")
            return False
    
    def getCachedVideoDetail(self, video_id: int) -> Optional[Dict[str, Any]]:
        """
        获取缓存的视频详情 - 与node-server命名一致
        """
        try:
            if not self.redis_client:
                return None
                
            cache_key = RedisKeys.VIDEO_DETAIL.format(video_id=video_id)
            return self.getCache(cache_key)
        except Exception as e:
            print(f"获取缓存视频详情失败: {str(e)}")
            return None
    
    def initializeCaches(self, db) -> bool:
        """
        初始化缓存（预热） - 与node-server功能一致
        """
        try:
            if not self.redis_client or not db:
                return False
            
            print("开始缓存预热...")
            
            # 预热热门视频ZSET
            # 获取前100个热门视频
            query = """
                SELECT * FROM videos_detail 
                WHERE status = 1 AND deleted_at IS NULL
                ORDER BY view_count DESC LIMIT 100
            """
            hot_videos = db.session.execute(query).fetchall()
            hot_video_list = []
            for video in hot_videos:
                video_dict = dict(video._mapping)
                # 解析JSON字段
                if video_dict.get('tags'):
                    try:
                        video_dict['tags'] = json.loads(video_dict['tags'])
                    except:
                        video_dict['tags'] = []
                if video_dict.get('actors'):
                    try:
                        video_dict['actors'] = json.loads(video_dict['actors'])
                    except:
                        video_dict['actors'] = []
                hot_video_list.append(video_dict)
            self.cacheHotVideos(hot_video_list)
            
            # 预热推荐视频ZSET
            # 获取前100个推荐视频
            query = """
                SELECT * FROM videos_detail 
                WHERE status = 1 AND deleted_at IS NULL
                ORDER BY collect_count DESC LIMIT 100
            """
            recommended_videos = db.session.execute(query).fetchall()
            recommended_video_list = []
            for video in recommended_videos:
                video_dict = dict(video._mapping)
                # 解析JSON字段
                if video_dict.get('tags'):
                    try:
                        video_dict['tags'] = json.loads(video_dict['tags'])
                    except:
                        video_dict['tags'] = []
                if video_dict.get('actors'):
                    try:
                        video_dict['actors'] = json.loads(video_dict['actors'])
                    except:
                        video_dict['actors'] = []
                recommended_video_list.append(video_dict)
            self.cacheRecommendedVideos(recommended_video_list)
            
            # 预热幻灯片视频
            query = """
                SELECT * FROM videos_detail 
                WHERE status = 1 AND deleted_at IS NULL
                ORDER BY view_count DESC LIMIT 5
            """
            slide_videos = db.session.execute(query).fetchall()
            slide_video_list = []
            for video in slide_videos:
                video_dict = dict(video._mapping)
                # 解析JSON字段
                if video_dict.get('tags'):
                    try:
                        video_dict['tags'] = json.loads(video_dict['tags'])
                    except:
                        video_dict['tags'] = []
                if video_dict.get('actors'):
                    try:
                        video_dict['actors'] = json.loads(video_dict['actors'])
                    except:
                        video_dict['actors'] = []
                slide_video_list.append(video_dict)
            self.cacheSlideVideos(slide_video_list)
            
            print("缓存预热完成")
            return True
        except Exception as e:
            print(f"缓存预热失败: {str(e)}")
            return False
    
    # 兼容原有静态方法
    @classmethod
    def cache_hot_videos(cls, videos: List[Dict]) -> None:
        """兼容原有cache_hot_videos静态方法"""
        instance = RedisUtils()
        instance.cacheHotVideos(videos)
    
    @classmethod
    def get_cached_hot_videos(cls, start: int = 0, end: int = -1) -> List[Dict]:
        """兼容原有get_cached_hot_videos静态方法"""
        instance = RedisUtils()
        return instance.getCachedHotVideos(start, end)
    
    @classmethod
    def cache_recommend_videos(cls, videos: List[Dict]) -> None:
        """兼容原有cache_recommend_videos静态方法"""
        instance = RedisUtils()
        instance.cacheRecommendedVideos(videos)
    
    @classmethod
    def get_cached_recommend_videos(cls, start: int = 0, end: int = -1) -> List[Dict]:
        """兼容原有get_cached_recommend_videos静态方法"""
        instance = RedisUtils()
        return instance.getCachedRecommendedVideos(start, end)
    
    @classmethod
    def cache_new_videos(cls, videos: List[Dict]) -> None:
        """兼容原有cache_new_videos静态方法"""
        instance = RedisUtils()
        instance.cacheLatestVideos(videos)
    
    @classmethod
    def get_cached_new_videos(cls) -> Optional[List[Dict]]:
        """兼容原有get_cached_new_videos静态方法"""
        instance = RedisUtils()
        return instance.getCachedLatestVideos()
    
    @classmethod
    def cache_slide_videos(cls, videos: List[Dict]) -> None:
        """兼容原有cache_slide_videos静态方法"""
        instance = RedisUtils()
        instance.cacheSlideVideos(videos)
    
    @classmethod
    def get_cached_slide_videos(cls) -> Optional[List[Dict]]:
        """兼容原有get_cached_slide_videos静态方法"""
        instance = RedisUtils()
        return instance.getCachedSlideVideos()
    
    @classmethod
    def get_zset_range(cls, key: str, start: int, end: int) -> List[str]:
        """兼容原有get_zset_range静态方法"""
        from app.config.redis import redis_client
        return redis_client.zrange(key, start, end)
    
    @classmethod
    def zadd(cls, key: str, mapping: Dict[str, float]) -> int:
        """兼容原有zadd静态方法"""
        from app.config.redis import redis_client
        return redis_client.zadd(key, mapping)
    
    @classmethod
    def sync_view_count_to_db(cls) -> None:
        """兼容原有sync_view_count_to_db静态方法"""
        try:
            # 内部导入，避免循环导入
            from app.models import Video
            from app.extensions import db
            from app.config.redis import redis_client
            
            instance = RedisUtils()
            video_ids = instance.getPendingViewUpdates()
            
            if video_ids:
                instance.syncViewCountToDB(db, video_ids)
        except Exception as e:
            print(f"同步观看次数到数据库失败: {str(e)}")


# 创建工具类实例
redis_utils = RedisUtils()